﻿using StateObserver.Core.Fluent;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;

namespace FluentStateObserver.Test
{   
    
    /// <summary>
    ///This is a test class for FluentStateObserverTest and is intended
    ///to contain all FluentStateObserverTest Unit Tests
    ///</summary>
    [TestClass()]
    public class FluentStateObserverTest
    {
        [TestMethod()]
        public void Should_Initialize_Values_Properly()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            Assert.AreEqual(obs, target.Observable, "Didn't set the underlying observable");
            Assert.AreEqual(0, target.CurrentStateIndex);
        }

        [TestMethod()]
        public void Should_Match_Beginning_And_Ending_Patterns()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            bool didMatch = false;
            target
                .StartsWith(x => x.TypeOfElement == ElementType.TestType1)
                .EndsWith(x => x.TypeOfElement == ElementType.TestType2)
                .OnMatch(x => didMatch = true);

            obs.ProcessBlocks();

            Assert.IsTrue(didMatch);
        }

        [TestMethod()]
        public void Shoud_Not_Match_Invalid_Beginning_And_Ending_Patterns()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            bool didMatch = false;
            target
                .StartsWith(x => x.TypeOfElement == ElementType.TestType1)
                .EndsWith(x => x.TypeOfElement == ElementType.TestType5)
                .OnMatch(x => didMatch = true);

            obs.ProcessBlocks();

            Assert.IsFalse(didMatch);
        }

        [TestMethod()]
        public void Should_Match_FollowedBy_Patterns()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            bool didMatch = false;
            target
                .StartsWith(x => x.TypeOfElement == ElementType.TestType1)
                .FollowedBy(x => x.TypeOfElement == ElementType.TestType3)
                .EndsWith(x => x.TypeOfElement == ElementType.TestType4)
                .OnMatch(x => didMatch = true);

            obs.ProcessBlocks();

            Assert.IsTrue(didMatch);
        }

        [TestMethod()]
        public void Should_Match_FollowedImmediately_Patterns()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            bool didMatch = false;
            target
                .StartsWith(x => x.TypeOfElement == ElementType.TestType1)
                .FollowedImmediatelyBy(x => x.TypeOfElement == ElementType.TestType2)
                .EndsWith(x => x.TypeOfElement == ElementType.TestType4)
                .OnMatch(x => didMatch = true);

            obs.ProcessBlocks();

            Assert.IsTrue(didMatch);
        }

        [TestMethod()]
        public void Should_Not_Match_Invalid_FollowedImmediatelyBy_Patterns()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            bool didMatch = false;
            target
                .StartsWith(x => x.TypeOfElement == ElementType.TestType1)
                .FollowedImmediatelyBy(x => x.TypeOfElement == ElementType.TestType3)
                .EndsWith(x => x.TypeOfElement == ElementType.TestType4)
                .OnMatch(x => didMatch = true);

            obs.ProcessBlocks();

            Assert.IsFalse(didMatch);
        }

        [TestMethod()]
        public void Should_Match_EndsImmediatelyWith_Patterns()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            bool didMatch = false;
            target
                .StartsWith(x => x.TypeOfElement == ElementType.TestType1)
                .EndsImmediatelyWith(x => x.TypeOfElement == ElementType.TestType2)
                .OnMatch(x => didMatch = true);

            obs.ProcessBlocks();

            Assert.IsTrue(didMatch);
        }

        [TestMethod()]
        public void Should_Not_Match_Invalid_EndsImmediatelyWith_Patterns()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            bool didMatch = false;
            target
                .StartsWith(x => x.TypeOfElement == ElementType.TestType1)
                .EndsImmediatelyWith(x => x.TypeOfElement == ElementType.TestType4)
                .OnMatch(x => didMatch = true);

            obs.ProcessBlocks();

            Assert.IsFalse(didMatch);
        }

        [TestMethod()]
        public void Should_Match_Multiple_Times_For_Repeated_Patterns()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            var matches = 0;
            target
                .StartsWith(x => x.TypeOfElement == ElementType.TestType1)
                .EndsWith(x => x.TypeOfElement == ElementType.TestType2)
                .OnMatch(x => matches++);

            obs.ProcessBlocks();

            Assert.AreEqual(2, matches);
        }

        [TestMethod()]
        public void Should_Match_Multiple_Clauses_In_Transition()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            bool didMatch = false;
            target
                .StartsWith(
                    x => x.TypeOfElement == ElementType.TestType1,
                    x => x.Message.Equals("Message 1"))
                .FollowedBy(
                    x => x.TypeOfElement == ElementType.TestType3,
                    x => x.Message.Equals("Message 3"))                
                .EndsWith(
                    x => x.TypeOfElement == ElementType.TestType4,
                    x => x.Message.Equals("Message 4"))                    
                .OnMatch(x => didMatch = true);

            obs.ProcessBlocks();

            Assert.IsTrue(didMatch);
        }

        [TestMethod()]
        public void Should_Not_Match_Invalid_Multiple_Clauses_In_Transition()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            bool didMatch = false;
            target
                .StartsWith(
                    x => x.TypeOfElement == ElementType.TestType1,
                    x => x.Message.Equals("Message 1"))
                .FollowedBy(
                    x => x.TypeOfElement == ElementType.TestType3,
                    x => x.Message.Equals("Message 1")) // Wrong clause
                .EndsWith(
                    x => x.TypeOfElement == ElementType.TestType4,
                    x => x.Message.Equals("Message 4"))
                .OnMatch(x => didMatch = true);

            obs.ProcessBlocks();

            Assert.IsFalse(didMatch);
        }

        [TestMethod()]
        public void Should_Match_Multiple_FollowedBy_Patterns()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            bool didMatch = false;
            target
                .StartsWith(x => x.TypeOfElement == ElementType.TestType1)
                .FollowedBy(x => x.TypeOfElement == ElementType.TestType2)
                .FollowedBy(x => x.TypeOfElement == ElementType.TestType3)
                .EndsWith(x => x.TypeOfElement == ElementType.TestType4)
                .OnMatch(x => didMatch = true);

            obs.ProcessBlocks();

            Assert.IsTrue(didMatch);
        }

        [TestMethod()]
        public void Should_Reset_When_A_Reset_Condition_Is_Satisfied()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            int matches = 0;
            target
                .StartsWith(x => x.TypeOfElement == ElementType.TestType1)                
                .FollowedBy(x => x.TypeOfElement == ElementType.TestType3)
                .ResetOn(x => x.TypeOfElement == ElementType.TestType2)
                .EndsWith(x => x.TypeOfElement == ElementType.TestType4)
                .OnMatch(x => matches++);

            obs.ProcessBlocks();

            Assert.AreEqual(0, matches);
        }

        [TestMethod()]
        public void Should_Not_Reset_When_A_Reset_Condition_Is_Not_Satisfied()
        {
            var obs = TestObservables.BasicObservable();
            var target = new FluentStateObserver<TestElement>(obs);

            int matches = 0;
            target
                .StartsWith(x => x.TypeOfElement == ElementType.TestType1)
                .EndsWith(x => x.TypeOfElement == ElementType.TestType3)
                .ResetOn(x => x.TypeOfElement == ElementType.TestType5)                
                .OnMatch(x => matches++);

            obs.ProcessBlocks();

            Assert.AreEqual(2, matches);
        }
    }
}
